package arc

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

type Reactor struct {
	Engine       *gin.Engine
	g            *gin.RouterGroup
	currentGroup *gin.RouterGroup
	gMap         map[string]*gin.RouterGroup
	Config       IConfig
	mtx          sync.RWMutex
}

func Start(ginMiddlewares ...gin.HandlerFunc) *Reactor {
	r := &Reactor{
		Engine: gin.New(),
		gMap:   make(map[string]*gin.RouterGroup),
	}
	// 使用错误处理
	r.Engine.Use(ErrorHandler())
	r.Engine.Use(ginMiddlewares...)

	return r
}

func (r *Reactor) LoadConf(filePath string, configPtr IConfig) *Reactor {
	if isExist(filePath) {
		viper.SetConfigFile(filePath)
		viper.SetConfigType("yaml")
		err := viper.ReadInConfig()
		if err != nil {
			fmt.Println("Failed to Viper ReadInConfig,Error:", err)
		}
		if err = viper.Unmarshal(configPtr); err != nil {
			fmt.Println("Viper Unmarshal Failed,Error:", err)
		}
	}
	r.Config = configPtr
	BeanFactory.Set(configPtr)
	return r
}

func (r *Reactor) GoRun() {
	r.applyAll()
	r.Engine.Run(r.Config.GetServer().GetAddr())
}

func (r *Reactor) Handle(httpMethod, path string, handler interface{}) *Reactor {
	if h := Convert(handler); h != nil {
		methods := strings.Split(httpMethod, ",")
		for _, method := range methods {
			r.g.Handle(method, path, h)
		}
	}
	return r
}

func (r *Reactor) getG() *gin.RouterGroup {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	return r.g
}

func (r *Reactor) getGroup() *gin.RouterGroup {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	return r.currentGroup
}

func (r *Reactor) setGroup(g *gin.RouterGroup) *Reactor {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	r.currentGroup = g
	return r
}

func (r *Reactor) setG(g *gin.RouterGroup) *Reactor {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	r.g = g
	return r
}
func (r *Reactor) setGMap(g *gin.RouterGroup) *Reactor {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	r.gMap[g.BasePath()] = g
	return r
}

func (r *Reactor) getGMap(group string) *gin.RouterGroup {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	return r.gMap[group]
}

func (r *Reactor) Group(name string) *Reactor {

	g := r.getGMap(name)
	if g == nil {
		g = r.Engine.Group(name)
		r.setG(g).setGMap(g).setGroup(g)
	} else {
		r.setG(g).setGroup(g)
	}
	return r
}

func (r *Reactor) GroupUse(group string, handlers ...gin.HandlerFunc) *Reactor {
	r.Group(group).getGroup().Use(handlers...)
	return r
}

func (r *Reactor) Mount(group string, classes ...IClass) *Reactor {
	if r.getGroup() == nil {
		r.Group(group)
	} else {
		g := r.getGroup().Group(group)
		r.setG(g).setGMap(g)
	}
	return r.OnlyMount(classes...)
}

func (r *Reactor) MountWithGroup(childGroup string, classes ...IClass) *Reactor {
	return r.
		setG(r.getGroup().Group(childGroup)).
		OnlyMount(classes...)
}

func (r *Reactor) OnlyMount(classes ...IClass) *Reactor {
	for _, class := range classes {
		class.Build(r)
		r.Beans(class)
	}
	return r
}

func (r *Reactor) SetGroup(group string) *Reactor {
	g := r.Engine.Group(group)
	return r.setGMap(g).setGroup(g)
}

func (r *Reactor) Beans(beans ...Bean) *Reactor {
	for _, b := range beans {
		BeanFactory.Set(b)
	}
	return r
}

func (r *Reactor) InjectConfig(cfgs ...interface{}) *Reactor {
	BeanFactory.InjectConfig(cfgs...)
	return r
}

func (r *Reactor) applyAll() {
	for t, v := range BeanFactory.GetBM() {
		if t.Elem().Kind() == reflect.Struct {
			BeanFactory.Apply(v.Interface())
		}
	}
}

func (r *Reactor) Task(task ...ITask) *Reactor {
	for _, t := range task {
		t.Start()
		BeanFactory.Set(t)
	}
	return r
}
